﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Qreed.Windows.Forms;
using System.IO;
using System.Reflection;
using Qreed.IO;
using Qreed.Diagnostics;
using GotLyrics.Properties;

namespace GotLyrics.Dialogs
{
    public partial class RenameFilesDialog : BaseDialog
    {
        private RenameFilesDialog() : base(null)
        {
            InitializeComponent();
        }

        public RenameFilesDialog(AudioFileContext[] audioFiles, GotLyricsMainForm mainForm) : base(mainForm)
        {
            _audioFiles = audioFiles;

            InitializeComponent();
            InitializeImageList();
            InitializePreview();

            DoubleBuffered = true;

            textBoxFilenameMask.Text = Settings.Default.RenameFilesMask;

            SetupToolTip();
            CreateIntellisenseWindow();
            
            UpdatePreview();
        }

        private void InitializePreview()
        {
            SuspendLayout();

            listPreview.Items.Clear();

            foreach (AudioFileContext audioFile in _audioFiles)
            {
                ListViewItem lvItem = new ListViewItem();
                lvItem.Checked = true;
                lvItem.Tag = audioFile;

                string oldFilename = Path.GetFileName(audioFile.Filename);
                lvItem.SubItems.Add(oldFilename);

                string newFilename = CreateFilename(textBoxFilenameMask.Text.Trim(), audioFile, lvItem);

                lvItem.SubItems.Add(newFilename);
                listPreview.Items.Add(lvItem);
            }

            ResumeLayout(true);
        }

        private void InitializeImageList()
        {
            Icon[] icons = new Icon[]
            {
                new Icon(Resources.SuccessIcon, statuslmageList.ImageSize),
                new Icon(Resources.FailedIcon, statuslmageList.ImageSize)
            };

            foreach (Icon icon in icons)
                statuslmageList.Images.Add(icon);
        }

        private void SetupToolTip()
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Insert tag names here to format your new filenames.");
            sb.AppendLine("Tag names should start and end with a '%' sign. All other text is considered as normal text.");
            sb.AppendLine("\nValid tag names are: ");

            foreach (string tag in _tagFields)
            {
                sb.Append("%");
                sb.Append(tag);
                sb.Append("%, ");
            }

            sb.Length -= 2;

            toolTipFilenameMask.TargetControl = textBoxFilenameMask;
            toolTipFilenameMask.ToolTipText = sb.ToString();

            //toolTip.SetToolTip(textBoxFilenameMask, sb.ToString());
            //toolTip.SetToolTip(groupBoxFilenameMask, sb.ToString());

            sb.Length = 0;

            sb.AppendLine("This is the preview table.\n");
            sb.AppendLine("The left column contains the original filename.");
            sb.AppendLine("The right column contains the new filename.");

            toolTipPreview.TargetControl = listPreview;
            toolTipPreview.ToolTipText = sb.ToString();

            //toolTip.SetToolTip(listPreview, sb.ToString());
            //toolTip.SetToolTip(groupBoxPreview, sb.ToString());
        }

        private const string DUPLICATEFILENAME = "Duplicate filename";

        private void UpdatePreview()
        {
            SuspendLayout();

            foreach (ListViewItem lvItem in listPreview.Items)
            {
                AudioFileContext audioFile = (AudioFileContext)lvItem.Tag;

                string newFilename = CreateFilename(textBoxFilenameMask.Text.Trim(), audioFile, lvItem);
                lvItem.SubItems[columnHeaderAfter.DisplayIndex].Text = newFilename;
            }

            // Compare with other listview items to check for duplicate filenames

            foreach (ListViewItem lvItem in listPreview.Items)
            {
                bool duplicateFilename = false;

                foreach (ListViewItem lvItem2 in listPreview.Items)
                {
                    if (lvItem == lvItem2) continue;

                    if (lvItem.SubItems[columnHeaderAfter.DisplayIndex].Text == 
                        lvItem2.SubItems[columnHeaderAfter.DisplayIndex].Text)
                    {
                        duplicateFilename = true;

                        lvItem2.SubItems[columnHeaderAfter.DisplayIndex].Text = DUPLICATEFILENAME;
                        lvItem2.ImageIndex = 1;
                    }
                }

                if (duplicateFilename)
                {
                    lvItem.SubItems[columnHeaderAfter.DisplayIndex].Text = DUPLICATEFILENAME;
                    lvItem.ImageIndex = 1;
                }
            }

            ResumeLayout(true);
        }

        

        
        private string CreateFilename(string mask, AudioFileContext audioFile, ListViewItem lvItem)
        {
            lvItem.ImageIndex = 0;

            foreach (string tag in _tagFields)
            {
                string search = "%" + tag + "%";
                string value = GetTagValue(tag, audioFile);

                if (string.IsNullOrEmpty(value))
                {
                    value = tag + " tag not found";

                    if (mask.Contains(search))
                        lvItem.ImageIndex = 1;
                }

                mask = mask.Replace(search, value);
            }

            string extention = Path.GetExtension(audioFile.Filename);

            if(WordCasing == WordCasingType.UpperCase)
                extention = extention.ToUpperInvariant();

            char[] invalidPathChars = Path.GetInvalidPathChars();

            int index = mask.IndexOfAny(invalidPathChars);

            while (index != -1)
            {
                mask = mask.Remove(index, 1);
                index = mask.IndexOfAny(invalidPathChars, Math.Max(index - 1, 0));
            }
            
            return mask + extention;
        }

        private string GetTagValue(string tag, AudioFileContext audioFile)
        {
            PropertyInfo pi = audioFile.Tags.GetType().GetProperty(tag);

            if (pi != null)
            {
                object o = pi.GetValue(audioFile.Tags, null);

                if (o != null)
                {
                    if (tag == "TrackNum" || tag == "TrackCount")
                        return ((short)o).ToString("00");

                    string value = o.ToString().Trim();

                    if (value != string.Empty)
                    {
                        value = WordCasingOptionsControl.Case(value, WordCasing);
                    }

                    if (ReplaceSpaces)
                        value = value.Replace(' ', '_');

                    return value;
                }
            }

            return null;
        }

        private void CreateIntellisenseWindow()
        {
            _intellisenseWindow = new IntellisenseWindow();
            _intellisenseWindow.TextBox = textBoxFilenameMask;
            _intellisenseWindow.SenseKey = Keys.D5;
            _intellisenseWindow.SenseModifierKey = Keys.Shift;
            _intellisenseWindow.IntellisenseInsert += new EventHandler<IntellisenseEventArgs>(OnIntellisenseInsert);

            foreach (string tag in _tagFields)
                _intellisenseWindow.ListBox.Items.Add(tag);
        }

        void OnIntellisenseInsert(object sender, IntellisenseEventArgs e)
        {
            e.IntellisenseText += "%";
        }

        private AudioFileContext[] _audioFiles;
        private IntellisenseWindow _intellisenseWindow;
        private string[] _tagFields = { "Album", "Artist", "Title", "TrackNum", "TrackCount", "Year" };

        private void textBoxFilenameMask_TextChanged(object sender, EventArgs e)
        {
            UpdatePreview();
        }

        protected override void OnClosed(EventArgs e)
        {
            Settings.Default.RenameFilesMask = textBoxFilenameMask.Text.Trim();

            base.OnClosed(e);
        }

        private bool ReplaceSpaces
        {
            get { return Settings.Default.RenameFilesReplaceSpaces; }
            set { Settings.Default.RenameFilesReplaceSpaces = value; }
        }

        private WordCasingType WordCasing
        {
            get { return (WordCasingType)Enum.Parse(typeof(WordCasingType), Settings.Default.RenameFilesWordCasing); }
            set { Settings.Default.RenameFilesWordCasing = value.ToString(); }
        }

        private void btOptions_Click(object sender, EventArgs e)
        {
            FilenameOptionsDialog dialog = new FilenameOptionsDialog(MainForm);

            dialog.ReplaceSpaces = ReplaceSpaces;
            dialog.WordCasing = WordCasing;

            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                ReplaceSpaces = dialog.ReplaceSpaces;
                WordCasing = dialog.WordCasing;
                UpdatePreview();
            }
        }

        protected bool CheckedDuplicateFilenames
        {
            get
            {
                foreach (ListViewItem item in listPreview.Items)
                {
                    if (item.Checked && item.SubItems[2].Text == DUPLICATEFILENAME)
                        return true;
                }

                return false;
            }
        }

        protected bool CheckedErrorStateItems
        {
            get
            {
                foreach (ListViewItem item in listPreview.Items)
                {
                    if (item.Checked && item.ImageIndex == 1)
                        return true;
                }

                return false;
            }
        }

        private string _commonBaseFolder;
        /// <summary>
        /// Gets the common base folder.
        /// </summary>
        /// <value>The common base folder.</value>
        public string CommonBaseFolder
        {
            get { return this._commonBaseFolder; }
        }

        private void btOk_Click(object sender, EventArgs e)
        {
            if (CheckedErrorStateItems)
            {
                MainForm.Log.Write("Filenames in error state.", "The preview list contains on or more items in error state.", LogLevel.Warning);

                if (MessageBox.Show(this,
                                    "Your preview list contains one or more filenames in error state.\r\n" +
                                    "This can happen when the file doesn't contains the required tag information.\r\n\r\n" +
                                    "Do you wish to continue?",
                                    "Filenames in error state.", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes)
                {
                    return;
                }
            }

            if (CheckedDuplicateFilenames)
            {
                MainForm.Log.Write("Duplicate filenames.", "The preview list contains on or more conflicting filenames.", LogLevel.Warning);

                if (MessageBox.Show(this,
                                    "Your preview list contains one or more conflicting filenames.\r\n" +
                                    "These files will be skipped if you proceed.\r\n\r\n" +
                                    "Do you wish to continue?",
                                    "Duplicate Filenames", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes)
                {
                    return;
                }
            }

            bool error = false;

            // List we'll use to get the common base folder
            List<string> destinationFolders = new List<string>();

            IList<ListViewItem> lvItems = columnHeaderCheckBox.GetCheckedItems();

            foreach (ListViewItem lvItem in lvItems)
            {
                AudioFileContext audioFile = (AudioFileContext)lvItem.Tag;

                string oldFilename = audioFile.Filename;
                string newFilename = lvItem.SubItems[2].Text;

                if (newFilename == DUPLICATEFILENAME)
                    continue;

                try
                {
                    string baseDirectory = Path.GetDirectoryName(oldFilename);
                    newFilename = IOHelper.MakeAbsolutePath(baseDirectory, newFilename);
                }
                catch (Exception ex)
                {
                    error = true;
                    MainForm.Log.Write("Error occured while creating absolute path for \"" + newFilename + "\".", ex);
                    break;
                }

                string details = string.Format("Old: \"{0}\"{2}New: \"{1}\"", oldFilename, newFilename, Environment.NewLine);

                // Create the destination directory if needed

                string destFolder = Path.GetDirectoryName(newFilename);
                destinationFolders.Add(destFolder);

                try
                {
                    if (!Directory.Exists(destFolder))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(newFilename));
                    }
                }
                catch (Exception ex)
                {
                    error = true;
                    MainForm.Log.Write("Error occured while creating folder \"" + destFolder + "\".", ex);
                    break;
                }

                MainForm.Log.Write("Renaming audiofile.", details, LogLevel.Info);

                try
                {
                    File.Move(oldFilename, newFilename);
                }
                catch (Exception ex)
                {
                    error = true;
                    MainForm.Log.Write("Error occured while renaming the file \"" + oldFilename + "\" to \"" + newFilename + "\".", ex);
                    break;
                }
            }

            if (!error)
            {
                try
                {
                    if(destinationFolders.Count > 0)
                        _commonBaseFolder = IOHelper.GetCommonBaseFolder(destinationFolders.ToArray());
                }
                catch (Exception ex)
                {
                    MainForm.Log.Write("Error occured while getting the common base folder.", ex);
                }

                DialogResult = DialogResult.OK;
                Close();
            }
        }
    }
}
